/*
 *  Copyright 2019-2020 Zheng Jie
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package me.landery.modules.security.rest;

import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.wf.captcha.base.Captcha;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.landery.annotation.rest.AnonymousDeleteMapping;
import me.landery.annotation.rest.AnonymousGetMapping;
import me.landery.annotation.rest.AnonymousPostMapping;
import me.landery.config.RsaProperties;
import me.landery.exception.BadRequestException;
import me.landery.modules.security.config.bean.LoginCodeEnum;
import me.landery.modules.security.config.bean.LoginProperties;
import me.landery.modules.security.config.bean.SecurityProperties;
import me.landery.modules.security.security.TokenProvider;
import me.landery.modules.security.service.dto.AuthUserDto;
import me.landery.modules.security.service.dto.JwtUserDto;
import me.landery.modules.security.service.OnlineUserService;
import me.landery.modules.security.service.dto.WebAuthUserDto;
import me.landery.modules.sinco.domain.Profit;
import me.landery.modules.sinco.domain.Recommend;
import me.landery.modules.sinco.domain.em.ProfitType;
import me.landery.modules.sinco.service.ProfitService;
import me.landery.modules.sinco.service.RecommendService;
import me.landery.modules.sinco.service.VipService;
import me.landery.modules.sinco.service.dto.RecommendDto;
import me.landery.modules.system.domain.Dept;
import me.landery.modules.system.domain.Job;
import me.landery.modules.system.domain.Role;
import me.landery.modules.system.domain.User;
import me.landery.modules.system.service.DeptService;
import me.landery.modules.system.service.JobService;
import me.landery.modules.system.service.RoleService;
import me.landery.modules.system.service.UserService;
import me.landery.modules.system.service.dto.RoleSmallDto;
import me.landery.modules.system.service.dto.UserDto;
import me.landery.modules.system.service.mapstruct.DeptMapper;
import me.landery.modules.system.service.mapstruct.JobMapper;
import me.landery.modules.system.service.mapstruct.RoleMapper;
import me.landery.utils.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.mail.search.SearchTerm;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author Zheng Jie
 * @date 2018-11-23
 * 授权、根据token获取用户详细信息
 */
@Slf4j
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
@Api(tags = "系统：系统授权接口")
public class AuthorizationController {

    private final PasswordEncoder passwordEncoder;
    private final SecurityProperties properties;
    private final RedisUtils redisUtils;
    private final OnlineUserService onlineUserService;
    private final UserService userService;
    private final TokenProvider tokenProvider;
    private final AuthenticationManagerBuilder authenticationManagerBuilder;

    private final RecommendService recommendService;

    private final ProfitService profitService;
    @Resource
    private LoginProperties loginProperties;

    private final RoleService roleService;
    private final RoleMapper roleMapper;

    private final DeptService deptService;
    private final DeptMapper deptMapper;

    private final JobService jobService;
    private final JobMapper jobMapper;



    @ApiOperation("登录授权")
    @AnonymousPostMapping(value = "/login")
    public ResponseEntity<Object> login(@Validated @RequestBody AuthUserDto authUser, HttpServletRequest request) throws Exception {
        // 密码解密
        String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, authUser.getPassword());
        // 查询验证码
        String code = (String) redisUtils.get(authUser.getUuid());
        // 清除验证码
        redisUtils.del(authUser.getUuid());
        if (StringUtils.isBlank(code)) {
            throw new BadRequestException("验证码不存在或已过期");
        }
        if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) {
            throw new BadRequestException("验证码错误");
        }
        // 查询用户信息
        UserDto user = userService.findByName(authUser.getUsername());
        if (user == null) {
            throw new BadRequestException("用户不存在");
        }

        Integer currentLevel =  Collections.min(roleService.findByUsersId(user.getId()).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList()));
        if (currentLevel == null || currentLevel == 999) {
            throw new BadRequestException("您没有权限登录");
        }
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(authUser.getUsername(), password);
        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // 生成令牌与第三方系统获取令牌方式
        // UserDetails userDetails = userDetailsService.loadUserByUsername(userInfo.getUsername());
        // Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        // SecurityContextHolder.getContext().setAuthentication(authentication);
        String token = tokenProvider.createToken(authentication);
        final JwtUserDto jwtUserDto = (JwtUserDto) authentication.getPrincipal();
        // 保存在线信息
        onlineUserService.save(jwtUserDto, token, request);
        // 返回 token 与 用户信息
        Map<String, Object> authInfo = new HashMap<String, Object>(2) {{
            put("token", properties.getTokenStartWith() + token);
            put("user", jwtUserDto);
        }};
        if (loginProperties.isSingleLogin()) {
            //踢掉之前已经登录的token
            onlineUserService.checkLoginOnUser(authUser.getUsername(), token);
        }
        return ResponseEntity.ok(authInfo);
    }

    @ApiOperation("web用户前端登录授权")
    @AnonymousPostMapping(value = "/weblogin")
    public ResponseEntity<Object> webLogin(@Validated @RequestBody WebAuthUserDto authUser, HttpServletRequest request) throws Exception {
        // 密码解密
        String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, authUser.getPassword());
        // 查询验证码
        String code = (String) redisUtils.get(authUser.getUuid());
        // 清除验证码
        redisUtils.del(authUser.getUuid());
        if (StringUtils.isBlank(code)) {
            throw new BadRequestException("验证码不存在或已过期");
        }
        if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) {
            throw new BadRequestException("验证码错误");
        }
        //判断username是否
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(authUser.getUsername(), password);
        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // 生成令牌与第三方系统获取令牌方式
        // UserDetails userDetails = userDetailsService.loadUserByUsername(userInfo.getUsername());
        // Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        // SecurityContextHolder.getContext().setAuthentication(authentication);
        String token = tokenProvider.createToken(authentication);
        final JwtUserDto jwtUserDto = (JwtUserDto) authentication.getPrincipal();
        // 保存在线信息
        onlineUserService.save(jwtUserDto, token, request);
        // 返回 token 与 用户信息
        Map<String, Object> authInfo = new HashMap<String, Object>(2) {{
            put("token", properties.getTokenStartWith() + token);
            put("user", jwtUserDto);
        }};
        if (loginProperties.isSingleLogin()) {
            //踢掉之前已经登录的token
            onlineUserService.checkLoginOnUser(authUser.getUsername(), token);
        }
        return ResponseEntity.ok(authInfo);
    }

    @ApiOperation("获取用户信息")
    @GetMapping(value = "/info")
    public ResponseEntity<Object> getUserInfo() {
        return ResponseEntity.ok(SecurityUtils.getCurrentUser());
    }

    @ApiOperation("web用户前端注册")
    @AnonymousPostMapping(value = "/register")
    @Transactional(rollbackFor = Exception.class)
    public ResponseEntity<Object> register(@Validated @RequestBody User resources,@RequestParam(value = "referCode",required = false) String referCode) {

        String code = resources.getAvatarPath();
        String phone = resources.getPhone();
        if (!redisUtils.hasKey(phone) || !StringUtils.equals(code,String.valueOf(redisUtils.get(phone)))){
            throw new BadRequestException("验证码错误");
        }

        if (!StringUtils.equals(resources.getCreateBy(),resources.getUpdateBy())){
            throw new BadRequestException("两次密码不一致");
        }
        resources.setPassword(passwordEncoder.encode(resources.getCreateBy()));

        if (referCode != null){
            Recommend recommend = recommendService.getByReferCode(referCode);
            if (recommend == null){
                throw new BadRequestException("邀请码不存在");
            }
            resources.setReferCode(referCode);
        }
        Set<Role> roles = new HashSet<>();
        roles.add(roleMapper.toEntity(roleService.findById(3L))); //sinco用户
        Dept dept = deptMapper.toEntity(deptService.findById(18L)); //用户部门
        resources.setDept(dept);
        resources.setRoles(roles);
        Set<Job> jobs = new HashSet<>();
        jobs.add(jobMapper.toEntity(jobService.findById(13L))); //用户职位
        resources.setJobs(jobs);
        userService.create(resources);

        String jsonStr = "{\"success\":true,\"content\":\"hello\"}";
        JSONObject json = new JSONObject(jsonStr);

        //处理推荐人逻辑
        if(referCode != null){
            profitService.doProfit(resources,referCode, ProfitType.NEW_USER_INVITE,0.0);
        }

        return ResponseEntity.ok(json);

    }

    @ApiOperation("web用户找回密码")
    @AnonymousPostMapping(value = "/forgot_password")
    public ResponseEntity<Object> forgotPassword(@Validated @RequestBody Map<String, String> map) {
        Map.Entry<String, String> entry = map.entrySet().iterator().next();
        if (!redisUtils.hasKey(map.get("phone")) || !StringUtils.equals(map.get("code"),String.valueOf(redisUtils.get(map.get("phone"))))){
            throw new BadRequestException("验证码错误");
        }
        UserDto userDto = userService.findByPhone(map.get("phone"));
        String username = userDto.getUsername();
        userDto.setPassword(passwordEncoder.encode(userDto.getPhone()));
        String jsonStr = "{\"success\":true,\"content\":\""+username+"\"}";
        JSONObject json = new JSONObject(jsonStr);
        return ResponseEntity.ok(json);
    }

//    @ApiOperation("实名认证身份证")
//    @AnonymousPostMapping(value = "/id_cert")
//    public ResponseEntity<Object> idCert(@Validated @RequestBody String phone) {
//
//    }

//    @ApiOperation("是否已经实名认证")
//    @AnonymousGetMapping(value = "/is_certed")
//    public ResponseEntity<Object> isCerted() {
//        userService.findById(SecurityUtils.getCurrentUserId());
//    }


    @ApiOperation("获取手机验证码")
    @AnonymousPostMapping(value = "/phone_code")
    public ResponseEntity<Object> getPhoneCode(@Validated @RequestBody String phone) {

        if (redisUtils.hasKey(phone)){
            return ResponseEntity.ok("验证码尚在有效期，请稍后重试");
        }

        // 您的短信账号
        String Account = "13281881260";
        // 您的短信账号密码
        String Password = "Admin123";
        // 是否需要状态报告
        String NeedStatus = "true";

        String code = RandomUtil.getFourBitRandom();
        String message = "您的验证码是"+code+"，如非本人操作，请忽略本短信！";//短信内容
        String ts =  YzmUtil.getNowDateStr();//时间戳

        Password = YzmUtil.getMD5(Account + Password + ts);// Md5签名(账号+密码+时间戳)
        HashMap params = new HashMap();
        //请求参数
        params.put("account",Account);
        params.put("pswd",Password);
        params.put("mobile",phone);
        params.put("msg",message);
        params.put("ts",ts);
        params.put("needstatus",NeedStatus);
        String rep = ZxHttpUtil.sendPostMessage("http://139.196.108.241:8080/Api/HttpSendSMYzm.ashx", params, "UTF-8");
        System.out.print(rep);
        JSONObject jsonObject = JSONUtil.parseObj(rep);
        if (!jsonObject.get("result_msg").equals("提交成功")){
            throw new BadRequestException("验证码获取失败");
        }
        //设定60s过期
        redisUtils.set(phone, code, 60);
        String jsonStr = "{\"success\":true,\"content\":\"hello\"}";
        JSONObject json = new JSONObject(jsonStr);
        return ResponseEntity.ok(json);
    }

    @ApiOperation("获取验证码")
    @AnonymousGetMapping(value = "/code")
    public ResponseEntity<Object> getCode() {
        // 获取运算的结果
        Captcha captcha = loginProperties.getCaptcha();
        String uuid = properties.getCodeKey() + IdUtil.simpleUUID();
        //当验证码类型为 arithmetic时且长度 >= 2 时，captcha.text()的结果有几率为浮点型
        String captchaValue = captcha.text();
        if (captcha.getCharType() - 1 == LoginCodeEnum.ARITHMETIC.ordinal() && captchaValue.contains(".")) {
            captchaValue = captchaValue.split("\\.")[0];
        }
        // 保存
        redisUtils.set(uuid, captchaValue, loginProperties.getLoginCode().getExpiration(), TimeUnit.MINUTES);
        // 验证码信息
        Map<String, Object> imgResult = new HashMap<String, Object>(2) {{
            put("img", captcha.toBase64());
            put("uuid", uuid);
        }};
        return ResponseEntity.ok(imgResult);
    }

    @ApiOperation("退出登录")
    @AnonymousDeleteMapping(value = "/logout")
    public ResponseEntity<Object> logout(HttpServletRequest request) {
        onlineUserService.logout(tokenProvider.getToken(request));
        return new ResponseEntity<>(HttpStatus.OK);
    }
}
